/* eslint-disable */
import * as THREE from "three";
import Stats from "stats.js";
import Detector from "./lib/Detector";
import ThreeBSP from "./lib/ThreeBSP";
import OrbitControls from "./lib/Three.OrbitControls";
import WindowResize from "./lib/ThreeX.windowResize";
import NativeApp from "../NativeApp";

require("./lib/RequestAnimationFrame");
require("vconsole");

const requestAnimationFrame = window.requestAnimationFrame;

// 地板的宽度，楼的占地面积
const floorWidth = 1600;
// 地板的长度
const floorLength = 1100;

// 楼层的高度
const floorHeight = -0.5; // 每一个层多加 200

// 楼层指示线条宽度
const lineWidth = 250;

// 墙壁厚度、宽度
const wallWidth = 20;

// 墙壁高度，楼层的高度
const wallHeight = 200;

// 玻璃的高度，和墙体求和的方块的高度，小于墙壁高度
const glassHeight = 110;

// 墙壁的长度，同地板长度
const leftWallLength = 1100;

// 旋转角度，0 或者 1.5
const wallAngle = 0;

const leftWallX = -801;
const rightWallX = 801;
const wallY = 100; // 一层加 200 。二层就是 100 + 200 = 300

const wallZ = 0;

const config = {
  // width: 700,
  width: window.innerWidth,
  height: window.innerHeight,
  // background: 0x4682b4,
  background: 0x282828,
  isMobile: false
};

/**
 * 构建一个对象
 * 通过 [] 来排列楼层
 */
class Building {
  constructor(dom, _option) {
    this.domContainer = dom;
    this.option = Object.assign(config, _option);

    // 摄像机
    this.camera = null;

    // 场景
    this.scene = null;

    // 渲染器
    this.renderer = null;

    //鼠标控制器
    this.controls = null;

    // 用来保存鼠标坐标信息
    this.mouse = new THREE.Vector2(1, 1);

    this.raycaster = new THREE.Raycaster();

    this.targetList = [];
  }

  onClickEvent = event => {
    const _this = this;
    const renderer = _this.renderer;
    const mouse = _this.mouse;
    const raycaster = _this.raycaster;
    const camera = _this.camera;
    const targetList = _this.targetList;
    if (event.touches) {
      // 移动端事件
      mouse.x =
        ((event.touches[0].pageX -
          renderer.domElement.getBoundingClientRect().left) /
          renderer.domElement.offsetWidth) *
          2 -
        1;
      mouse.y =
        -(
          (event.touches[0].pageY -
            renderer.domElement.getBoundingClientRect().top) /
          renderer.domElement.offsetHeight
        ) *
          2 +
        1;
    } else {
      mouse.x =
        ((event.clientX - renderer.domElement.getBoundingClientRect().left) /
          renderer.domElement.offsetWidth) *
          2 -
        1;
      mouse.y =
        -(
          (event.clientY - renderer.domElement.getBoundingClientRect().top) /
          renderer.domElement.offsetHeight
        ) *
          2 +
        1;
    }

    // 通过摄像机和鼠标位置更新射线
    raycaster.setFromCamera(mouse, camera);

    // 计算物体和射线的焦点
    const intersects = raycaster.intersectObjects(targetList);

    for (let i = 0; i < intersects.length; i++) {
      intersects[i].object.material.color.set(0xff0000);
      console.log(intersects[i]);
      const otherObj = targetList.filter(t => {
        return t.uuid !== intersects[i].object.uuid;
      });

      otherObj.forEach(t => {
        t["material"].color.set(0xffffff);
      });
    }
  };

  bindEvent() {
    const _this = this;
    const onClickEvent = _this.onClickEvent;

    // click 事件绑定
    _this.renderer.domElement.addEventListener(
      "mousedown",
      onClickEvent,
      false
    );

    // 手机端事件
    _this.renderer.domElement.addEventListener(
      "touchstart",
      onClickEvent,
      false
    );
    _this.windowResize = new WindowResize(_this.renderer, _this.camera);
    // THREEx.WindowResize(_this.renderer, _this.camera);
  }

  start() {
    const _this = this;
    _this.initScene();
    _this.initCamera();
    _this.initRender();
    _this.checkUserAgent();
    _this.initHelpGrid();
    _this.initControls();
    _this.initLight();
    _this.initObject();
    _this.bindEvent();
    NativeApp.setUpBridge({ index: 1 }, function(resultData) {
      console.log(resultData);
    });
    const stats = new Stats();
    stats.showPanel(1); // 0: fps, 1: ms, 2: mb, 3+: custom
    document.body.appendChild(stats.dom);
    animate();
    function animate() {
      stats.begin();
      _this.render();
      stats.end();
      requestAnimationFrame(animate);
    }
  }

  render() {
    const _this = this,
      controls = _this.controls,
      camera = _this.camera,
      scene = _this.scene,
      renderer = _this.renderer;

    renderer.render(scene, camera);
    controls.update();
  }

  /**
   *  创建一个场景
   */
  initScene() {
    this.scene = new THREE.Scene();
  }

  /**
   * 初始化 three 对象渲染器
   */
  initRender() {
    const _this = this;
    if (Detector.webgl)
      _this.renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: false
      });
    else _this.renderer = new THREE.CanvasRenderer();

    // 设置渲染的尺寸
    _this.renderer.setSize(_this.option.width, _this.option.height);

    _this.domContainer.appendChild(_this.renderer.domElement);
    // 设置背景的颜色
    _this.renderer.setClearColor(_this.option.background);
  }

  /**
   * 初始化摄像机
   * 并设置 视场, 视野长宽比例, 可见远近范围
   */
  initCamera() {
    const _this = this,
      width = _this.option.width,
      height = _this.option.height;
    _this.camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
    _this.camera.name = "buildingCamera";

    // 设置摄像机的位置
    _this.camera.position.set(1300, 3500, 3600);

    // 摄像机观察的方向-> 场景位置
    _this.camera.lookAt(_this.scene.position);

    _this.scene.add(_this.camera);
  }

  checkUserAgent() {
    let n = navigator.userAgent;
    if (
      n.match(/Android/i) ||
      n.match(/webOS/i) ||
      n.match(/iPhone/i) ||
      n.match(/iPad/i) ||
      n.match(/iPod/i) ||
      n.match(/BlackBerry/i)
    ) {
      config.isMobile = true;
      // this.camera.position.set(420, 420, 420);
      this.renderer.shadowMap.enabled = false;
    }
  }

  /**
   * 创建辅助网格线
   */
  initHelpGrid() {
    return;

    const _this = this;
    const gridHelper = new THREE.GridHelper(8000, 100, 0x09719f, 0x09719f);
    _this.scene.add(gridHelper);
  }

  /**
   * 初始化控制器，控制旋转等
   */
  initControls() {
    const _this = this;
    const controls = new OrbitControls(_this.camera, _this.renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.25;
    controls.rotateSpeed = 0.35;
    _this.controls = controls;
  }

  /**
   * 灯光初始化
   */
  initLight = function() {
    /*
      AmbientLight: 环境光，基础光源，它的颜色会被加载到整个场景和所有对象的当前颜色上。
      PointLight：点光源，朝着所有方向都发射光线
      SpotLight ：聚光灯光源：类型台灯，天花板上的吊灯，手电筒等
      DirectionalLight：方向光，又称无限光，从这个发出的光源可以看做是平行光.
    */
    const _this = this;
    // 位置不同，方向光作用于物体的面也不同，看到的物体各个面的颜色也不同
    // A start, 第二个参数是光源强度
    const directionalLight = new THREE.DirectionalLight(0xffffff, 1); //模拟远处类似太阳的光源
    directionalLight.position.set(0, 100, 0).normalize();
    _this.scene.add(directionalLight);
    // A end
    const ambient = new THREE.AmbientLight(0xffffff, 1); // AmbientLight,影响整个场景的光源
    ambient.position.set(0, 0, 0);
    _this.scene.add(ambient);
  };

  /**
   * 创建主体对象
   */
  initObject() {
    this.createWallMaterial();
    this.createFloor();

    this.createFloor2(2);

    this.createFloor2(3);
    // this.createFloor2(3);

    this.createFloor2(4);

    this.createFloor2(5);

    this.createFloor2(6);
    this.createFloor2(7);
    this.createFloor2(8);
    this.createFloor3(8);
  }

  /**
   * 创建墙纹理
   */
  createWallMaterial() {
    const matArrayA = [],
      matArrayB = [];
    matArrayA.push(new THREE.MeshPhongMaterial({ color: 0xafc0ca })); //前  0xafc0ca :灰色
    matArrayA.push(new THREE.MeshPhongMaterial({ color: 0xafc0ca })); //后
    matArrayA.push(new THREE.MeshPhongMaterial({ color: 0xd6e4ec })); //上  0xd6e4ec： 偏白色
    matArrayA.push(new THREE.MeshPhongMaterial({ color: 0xd6e4ec })); //下
    matArrayA.push(new THREE.MeshPhongMaterial({ color: 0xafc0ca })); //左    0xafc0ca :灰色
    matArrayA.push(new THREE.MeshPhongMaterial({ color: 0xafc0ca })); //右

    matArrayB.push(new THREE.MeshPhongMaterial({ color: 0xafc0ca })); //前  0xafc0ca :灰色
    matArrayB.push(new THREE.MeshPhongMaterial({ color: 0x9cb2d1 })); //后  0x9cb2d1：淡紫
    matArrayB.push(new THREE.MeshPhongMaterial({ color: 0xd6e4ec })); //上  0xd6e4ec： 偏白色
    matArrayB.push(new THREE.MeshPhongMaterial({ color: 0xd6e4ec })); //下
    matArrayB.push(new THREE.MeshPhongMaterial({ color: 0xafc0ca })); //左   0xafc0ca :灰色
    matArrayB.push(new THREE.MeshPhongMaterial({ color: 0xafc0ca })); //右

    this.matArrayB = matArrayB;
    this.matArrayA = matArrayA;
  }

  /**
   * 返回一个墙对象
   * @param {*} width
   * @param {*} height
   * @param {*} depth
   * @param {*} angle
   * @param {*} material
   * @param {*} x
   * @param {*} y
   * @param {*} z
   * @param {*} rotationZ
   */
  returnWallObject(width, height, depth, angle, material, x, y, z, rotationZ) {
    const cubeGeometry = new THREE.BoxGeometry(width, height, depth);
    const cube = new THREE.Mesh(cubeGeometry, material);
    cube.position.set(x, y, z);
    cube.rotation.y += angle * Math.PI;
    if (rotationZ) {
      cube.rotation.z = Math.PI * rotationZ;
    }
    return cube;
  }

  /**
   * 返回一个玻璃对象
   */
  returnGlassObject() {
    // 茶色：0x58ACFA   透明玻璃色：0XECF1F3
    const glass_material = new THREE.MeshBasicMaterial({ color: 0xecf1f3 });
    glass_material.opacity = 0.4;
    glass_material.transparent = true;
    return glass_material;
  }
  /**
   * 创建一层
   */
  createFloor() {
    const _this = this;
    const loader = new THREE.TextureLoader();
    const glass_material = _this.returnGlassObject();
    const matArrayB = _this.matArrayB;
    const group = new THREE.Group();
    // 创建地板
    loader.load("images/floor.jpg", function(texture) {
      texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
      texture.repeat.set(10, 10);
      const floorGeometry = new THREE.BoxGeometry(floorWidth, floorLength, 1);
      const floorMaterial = new THREE.MeshBasicMaterial({
        map: texture,
        side: THREE.DoubleSide
      });
      const floor = new THREE.Mesh(floorGeometry, floorMaterial);
      floor.position.y = floorHeight;
      floor.rotation.x = Math.PI / 2;
      group.add(floor);
    });

    /**
     * 一楼左面墙
     */
    const left_wall = _this.returnWallObject(
      20,
      200,
      1100,
      0,
      matArrayB,
      -801,
      100,
      0
    );
    const left_cube = _this.returnWallObject(
      20,
      110,
      1100,
      0,
      matArrayB,
      -801,
      100,
      0
    );
    const resultLeft = _this.createResultBsp(left_wall, left_cube, 1);
    const glassLeft = _this.createCubeWall(
      1,
      110,
      1100,
      0,
      glass_material,
      -801,
      100,
      0
    );

    group.add(resultLeft);
    group.add(glassLeft);

    /**
     * 一楼后面墙
     */
    const after_wall = _this.returnWallObject(
      20,
      200,
      1600,
      1.5,
      matArrayB,
      0,
      100,
      -540
    );
    group.add(after_wall);

    /**
     * 一楼右面墙
     */
    const right_wall = _this.returnWallObject(
      20,
      200,
      1100,
      1,
      matArrayB,
      801,
      100,
      0
    );
    const right_cube = _this.returnWallObject(
      20,
      110,
      1100,
      0,
      matArrayB,
      801,
      100,
      0
    );
    const resultRight = _this.createResultBsp(right_wall, right_cube, 1);
    const glassRight = _this.createCubeWall(
      1,
      110,
      1100,
      0,
      glass_material,
      801,
      100,
      0
    );
    group.add(resultRight);
    group.add(glassRight);

    // 带门的墙，前面墙
    const front_wall = _this.returnWallObject(
      1600,
      200,
      10,
      0,
      matArrayB,
      0,
      100,
      540
    );
    // 门框
    const door_cube = _this.returnWallObject(
      100,
      180,
      10,
      0,
      matArrayB,
      0,
      90,
      540
    );
    const frontResult = _this.createResultBsp(front_wall, door_cube, 1);

    loader.load("images/door_right.png", function(texture) {
      const doorgeometry = new THREE.BoxGeometry(100, 180, 2);
      const doormaterial = new THREE.MeshBasicMaterial({
        map: texture,
        color: 0xffffff
      });
      const doorGroup = new THREE.Object3D();
      doormaterial.opacity = 1.0;
      doormaterial.transparent = true;
      const door = new THREE.Mesh(doorgeometry, doormaterial);
      const door1 = door.clone();
      door.position.set(-50, 0, 0);
      door1.position.set(50, 0, 0);
      door1.visible = false;
      doorGroup.add(door);
      doorGroup.add(door1);
      doorGroup.position.set(50, 90, 540);
      group.add(doorGroup);
      group.add(frontResult);
      group.name = "firstFloor";
      group.position.set(0, 0, 0);
      _this.scene.add(group);
      group.cursor = "pointer";
    });
    _this.drawTextShow({
      index: 1
    });
  }

  // 绘制2-最后一层
  createFloor2(index = 2) {
    // index 最小是2
    index = index - 1;
    const _this = this;
    const loader = new THREE.TextureLoader();
    const group = new THREE.Group();
    const glass_material = _this.returnGlassObject();
    const matArrayB = _this.matArrayB;
    let left_wallY = wallY;

    loader.load("images/floor.jpg", function(texture) {
      texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
      texture.repeat.set(10, 10);
      const floorGeometry = new THREE.BoxGeometry(1600, 1100, 1);
      const floorMaterial = new THREE.MeshBasicMaterial({
        map: texture,
        side: THREE.DoubleSide
      });
      const floor = new THREE.Mesh(floorGeometry, floorMaterial);
      floor.position.y = floorHeight;
      floor.rotation.x = Math.PI / 2;
      group.add(floor);
    });

    /**
     * 左墙
     */
    const left_wall = _this.returnWallObject(
      wallWidth,
      wallHeight,
      leftWallLength,
      wallAngle,
      matArrayB,
      leftWallX,
      left_wallY,
      wallZ
    );

    const left_cube = _this.returnWallObject(
      wallWidth,
      glassHeight,
      leftWallLength,
      wallAngle,
      matArrayB,
      leftWallX,
      left_wallY,
      wallZ
    );

    // 玻璃容器和整个墙体组合出一个空洞放置玻璃
    const leftResult = _this.createResultBsp(left_wall, left_cube, 1);

    // 创建玻璃
    const leftGlass = _this.createCubeWall(
      // 玻璃厚度 1
      1,
      wallHeight,
      leftWallLength,
      wallAngle,
      glass_material,
      leftWallX,
      // 高度需要递增
      left_wallY,
      wallZ
    );
    group.add(leftResult);
    group.add(leftGlass);

    /**
     * 后面墙
     */
    const after_wall = _this.returnWallObject(
      20,
      200,
      1600,
      1.5,
      matArrayB,
      0,
      left_wallY,
      -540
    );

    const after_cube = _this.returnWallObject(
      20,
      110,
      1600,
      1.5,
      matArrayB,
      0,
      left_wallY,
      -540
    );
    const afterResult = _this.createResultBsp(after_wall, after_cube, 1);
    const afterGlass = _this.createCubeWall(
      1,
      110,
      1600,
      1.5,
      glass_material,
      0,
      left_wallY,
      -540
    );
    group.add(afterResult);
    group.add(afterGlass);

    /**
     * 右面墙
     */
    const right_wall = _this.returnWallObject(
      wallWidth,
      wallHeight,
      // 左右墙宽度相同
      leftWallLength,
      0,
      matArrayB,
      rightWallX,
      left_wallY,
      0
    );

    const right_cube = _this.returnWallObject(
      20,
      110,
      1100,
      0,
      matArrayB,
      801,
      left_wallY,
      0
    );
    const rightResult = _this.createResultBsp(right_wall, right_cube, 1);
    const rightGlass = _this.createCubeWall(
      1,
      110,
      1100,
      0,
      glass_material,
      801,
      left_wallY,
      0
    );

    group.add(rightResult);
    group.add(rightGlass);

    // 前面墙
    const before_wall = _this.returnWallObject(
      20,
      200,
      1600,
      1.5,
      matArrayB,
      0,
      left_wallY,
      540
    );

    const before_cube = _this.returnWallObject(
      20,
      110,
      1600,
      1.5,
      matArrayB,
      0,
      left_wallY,
      540
    );

    const beforeResult = _this.createResultBsp(before_wall, before_cube, 1);
    const beforeGlass = _this.createCubeWall(
      1,
      110,
      1600,
      1.5,
      glass_material,
      0,
      left_wallY,
      540
    );
    group.add(beforeResult);
    group.add(beforeGlass);
    group.position.set(0, index * 200, 0);
    this.scene.add(group);

    this.drawTextShow({
      index: index + 1
    });
  }

  // 绘制顶楼，只有一个地板
  createFloor3(index = 2) {
    const _this = this;
    const loader = new THREE.TextureLoader();
    loader.load("images/floor.jpg", function(texture) {
      texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
      texture.repeat.set(10, 10);
      const floorGeometry = new THREE.BoxGeometry(1600, 1100, 1);
      const floorMaterial = new THREE.MeshBasicMaterial({
        map: texture,
        side: THREE.DoubleSide
      });
      const floor = new THREE.Mesh(floorGeometry, floorMaterial);
      // 最后一层楼只有楼顶
      floor.position.y = index * 200 - 1;
      floor.rotation.x = Math.PI / 2;
      _this.scene.add(floor);
    });
  }

  /**
   * 墙上挖门，通过两个几何体生成BSP对象
   * @param {*} bsp
   * @param {*} less_bsp
   * @param {*} mat
   */
  createResultBsp(bsp, less_bsp, mat) {
    let material = null;
    switch (mat) {
      case 1:
        material = new THREE.MeshPhongMaterial({
          color: 0x9cb2d1,
          specular: 0x9cb2d1,
          shininess: 30,
          transparent: true,
          opacity: 1
        });
        break;
      case 2:
        material = new THREE.MeshPhongMaterial({
          color: 0xafc0ca,
          specular: 0xafc0ca,
          shininess: 30,
          transparent: true,
          opacity: 1
        });
        break;
      default:
    }

    let sphere1BSP = new ThreeBSP(bsp);
    let cube2BSP = new ThreeBSP(less_bsp); //0x9cb2d1 淡紫,0xC3C3C3 白灰 , 0xafc0ca灰
    let resultBSP = sphere1BSP.subtract(cube2BSP);
    let result = resultBSP.toMesh(material);
    result.material.flatshading = THREE.FlatShading;
    result.geometry.computeFaceNormals(); //重新计算几何体侧面法向量
    result.geometry.computeVertexNormals();
    result.material.needsUpdate = true; //更新纹理
    result.geometry.buffersNeedUpdate = true;
    result.geometry.uvsNeedUpdate = true;
    // this.scene.add(result);
    return result;
  }

  /**
   * 创建墙
   * @param {*} width
   * @param {*} height
   * @param {*} depth
   * @param {*} angle
   * @param {*} material
   * @param {*} x
   * @param {*} y
   * @param {*} z
   */
  createCubeWall(width, height, depth, angle, material, x, y, z) {
    const cubeGeometry = new THREE.BoxGeometry(width, height, depth);
    const cube = new THREE.Mesh(cubeGeometry, material);
    cube.position.x = x;
    cube.position.y = y;
    cube.position.z = z;
    cube.rotation.y += angle * Math.PI; //-逆时针旋转,+顺时针
    // this.scene.add(cube);
    return cube;
  }

  drawTextShow({ index }) {
    const _this = this;
    const loader = new THREE.FontLoader();
    const text = `F ${index}`;
    loader.load("fonts/helvetiker_regular.typeface.json", function(font) {
      const textGeometry = new THREE.TextGeometry(text, {
        font: font,
        size: 60,
        height: 6,
        curveSegments: 12,
        bevelEnabled: true,
        bevelThickness: 10,
        bevelSize: 4, // 字体粗细
        bevelSegments: 5
      });

      const textGroup = new THREE.Object3D();
      const material = new THREE.LineBasicMaterial({ color: 0x4682b4 });
      const geometry = new THREE.Geometry();
      geometry.vertices.push(
        new THREE.Vector3(
          floorWidth / 2,
          wallHeight / 2 + (index - 1) * wallHeight,
          0
        )
      );
      geometry.vertices.push(
        new THREE.Vector3(
          floorWidth / 2 + lineWidth,
          wallHeight / 2 + (index - 1) * wallHeight,
          0
        )
      );

      const line = new THREE.Line(geometry, material);
      const doormaterial = new THREE.MeshBasicMaterial({
        color: 0xffffff
      });
      const textMesh = new THREE.Mesh(textGeometry, doormaterial);
      textMesh.name = text;

      // 调节楼层文字标识定位
      textMesh.position.x = floorWidth / 2 + lineWidth + 80;
      textMesh.position.y = wallHeight / 4 + (index - 1) * wallHeight;
      textMesh.position.z = 0;

      textGroup.add(line);
      textGroup.add(textMesh);
      _this.targetList.push(textMesh);
      _this.scene.add(textGroup);
    });
  }
}

export default Building;
